package com.uduemc.biso.node.web.api.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.github.pagehelper.PageInfo;
import com.uduemc.biso.core.extities.center.*;
import com.uduemc.biso.core.utils.RedisUtil;
import com.uduemc.biso.core.utils.RestResult;
import com.uduemc.biso.core.utils.ResultUtil;
import com.uduemc.biso.node.core.common.entities.SSL;
import com.uduemc.biso.node.core.common.feign.CSiteFeign;
import com.uduemc.biso.node.core.entities.*;
import com.uduemc.biso.node.core.entities.custom.Article;
import com.uduemc.biso.node.core.entities.custom.ProductDataTableForList;
import com.uduemc.biso.node.core.feign.NodeSiteFeign;
import com.uduemc.biso.node.core.property.GlobalProperties;
import com.uduemc.biso.node.core.utils.RestResultUtil;
import com.uduemc.biso.node.core.utils.SiteUrlUtil;
import com.uduemc.biso.node.web.api.component.RequestHolder;
import com.uduemc.biso.node.web.api.pojo.SiteBaseInfo;
import com.uduemc.biso.node.web.api.service.*;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class SiteServiceImpl implements SiteService {

//    final Logger logger = LoggerFactory.getLogger(SiteServiceImpl.class);

    @Resource
    private NodeSiteFeign nodeSiteFeign;

    @Resource
    private CSiteFeign cSiteFeign;

    @Resource
    private RequestHolder requestHolder;

    @Resource
    private HostTypeService hostTypeServiceImpl;

    @Resource
    private SConfigService sConfigServiceImpl;

    @Resource
    private LanguageService languageServiceImpl;

    @Resource
    private DomainService domainServiceImpl;

    @Resource
    private GlobalProperties globalProperties;

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private PageService pageServiceImpl;

    @Resource
    private CategoryService categoryServiceImpl;

    @Resource
    private SystemService systemServiceImpl;

    @Resource
    private ArticleService articleServiceImpl;

    @Resource
    private ProductService productServiceImpl;

    @Resource
    private CenterService centerServiceImpl;

    @Resource
    private HostService hostServiceImpl;

    @Override
    public Site getDefaultSite(long hostId) throws IOException {
        RestResult restResult = nodeSiteFeign.findDefaultSite(hostId);
        Site site = ResultUtil.data(restResult, Site.class);
        if (site == null) {
            return null;
        }
        return site;
    }

    @Override
    public List<Site> getSiteList() {
        return requestHolder.getSites();
    }

    @Override
    public SiteBaseInfo getSiteBaseInfo() throws IOException {
        SiteBaseInfo siteBaseInfo = new SiteBaseInfo();
        Host host = requestHolder.getHost();
        // 网站状态：
        siteBaseInfo.setStatus(host.getTrial().shortValue());
        // 网站类型：
        SysHostType sysHostType = hostTypeServiceImpl.getInfoById(host.getTypeId().longValue());
        siteBaseInfo.setType(sysHostType == null ? "未知" : sysHostType.getName());
        // 网站有效期限：
        siteBaseInfo.setExpiry(
                host.getEndAt() == null ? LocalDateTime.now().plusYears(1).toInstant(ZoneOffset.of("+8")).toEpochMilli() : host.getEndAt().getTime());
        // 语言支持：
        List<Map<String, Object>> languages = new ArrayList<>();
        for (Site site : requestHolder.getSites()) {
            SConfig sConfig = sConfigServiceImpl.getInfoBySiteId(site.getId());
            Map<String, Object> data = new HashMap<>();
            data.put("site_id", site.getId().longValue());
            data.put("site_status", site.getStatus().shortValue());
            data.put("s_config_id", sConfig.getId().longValue());
            data.put("s_config_status", sConfig.getStatus().shortValue());
            data.put("language_name", languageServiceImpl.getLanguageBySite(site).getName());
            languages.add(data);
        }
        siteBaseInfo.setLanguages(languages);

        // 默认绑定域名：
        HDomain hDomain = requestHolder.getDefaultDomain();
        siteBaseInfo.setDomain(hDomain.getDomainName());
        // 节点IP
        SysServer selfSysServer = centerServiceImpl.selfSysServer();
        siteBaseInfo.setIp(selfSysServer.getIpaddress());
        // 绑定的域名：
        siteBaseInfo.setBind_domain(domainServiceImpl.bindIndos());
        return siteBaseInfo;
    }

    @Override
    public SiteBaseInfo getSiteBaseInfoOnlyLanguages() throws IOException {
        SiteBaseInfo siteBaseInfo = new SiteBaseInfo();
        // 语言支持：
        List<Map<String, Object>> languages = new ArrayList<>();
        for (Site site : requestHolder.getSites()) {
            SConfig sConfig = sConfigServiceImpl.getInfoBySiteId(site.getId());
            Map<String, Object> data = new HashMap<>();
            data.put("site_id", site.getId().longValue());
            data.put("site_status", site.getStatus().shortValue());
            data.put("s_config_id", sConfig.getId().longValue());
            data.put("s_config_status", sConfig.getStatus().shortValue());
            data.put("language_name", languageServiceImpl.getLanguageBySite(site).getName());
            languages.add(data);
        }
        siteBaseInfo.setLanguages(languages);
        return siteBaseInfo;
    }

    @Override
    public Site getInfoById(long id) throws IOException {
        RestResult restResult = nodeSiteFeign.findOne(id);
        return RestResultUtil.data(restResult, Site.class);
    }

    @Override
    public Site getInfoById(long hostId, long id) throws IOException {
        RestResult restResult = nodeSiteFeign.findByHostIdAndId(hostId, id);
        return RestResultUtil.data(restResult, Site.class);
    }

    @Override
    public Site update(Site site) throws IOException {
        RestResult restResult = nodeSiteFeign.updateAllById(site);
        return RestResultUtil.data(restResult, Site.class);
    }

    @Override
    public List<Site> getSiteList(long hostId) throws IOException {
        String KEY = globalProperties.getNodeRedisKey().getSiteListByHostId(hostId);
        @SuppressWarnings("unchecked")
        List<Site> data = (List<Site>) redisUtil.get(KEY);
        if (CollectionUtils.isEmpty(data)) {
            RestResult restResult = cSiteFeign.findSiteListByHostId(hostId);
            @SuppressWarnings("unchecked")
            List<Site> result = (List<Site>) RestResultUtil.data(restResult, new TypeReference<List<Site>>() {
            });
            redisUtil.set(KEY, result, 3600 * 12);
            return result;
        }
        return data;
    }

    @Override
    public List<Site> getOkSiteList(long hostId) throws IOException {
        List<Site> siteList = getSiteList(hostId);
        List<Site> result = new ArrayList<>();
        for (Site site : siteList) {
            if (site.getStatus() != null && site.getStatus().shortValue() == (short) 1) {
                result.add(site);
            }
        }
        return result;
    }

    @Override
    public List<Site> resetSiteList(long hostId) throws IOException {
        String KEY = globalProperties.getNodeRedisKey().getSiteListByHostId(hostId);
        RestResult restResult = nodeSiteFeign.findByHostId(hostId);
        @SuppressWarnings("unchecked")
        List<Site> result = (List<Site>) RestResultUtil.data(restResult, new TypeReference<List<Site>>() {
        });
        redisUtil.set(KEY, result, 3600 * 12);
        return result;
    }

    @Override
    public String siteUrl() throws IOException {
        String domain = requestHolder.getDefaultDomain().getDomainName();
        if (StrUtil.isBlank(domain)) {
            return null;
        }
        Site defaultSite = getDefaultSite(requestHolder.getHost().getId());
        if (defaultSite == null) {
            return null;
        }
        SSL infoSSL = domainServiceImpl.infoSSL(requestHolder.getDefaultDomain().getHostId(), requestHolder.getDefaultDomain().getId());
        boolean ssl = infoSSL == null ? false : true;
        Host host = requestHolder.getHost();
        HConfig hConfig = hostServiceImpl.getCurrentHostConfig();
        String result = null;
        if (defaultSite.getId().longValue() == requestHolder.getCurrentSite().getId().longValue()) {
            result = SiteUrlUtil.getSitePathBySysdomainAccess(domain, ssl, hConfig, host);
        } else {
            SysLanguage languageBySite = languageServiceImpl.getLanguageBySite(requestHolder.getCurrentSite());
            result = SiteUrlUtil.getSitePathBySysdomainAccess(domain, ssl, languageBySite.getLanNo(), hConfig, host);
        }

        return result;
    }

    @Override
    public List<String> siteUrls(long domainId) throws IOException {
        Long hostId = requestHolder.getHost().getId();
        Long siteId = requestHolder.getCurrentSite().getId();
        return siteUrls(hostId, siteId, domainId);
    }

    @Override
    public List<String> siteUrls(long hostId, long siteId, long domainId)
            throws IOException {
        Site site = getInfoById(hostId, siteId);
        return siteUrls(site, domainId);
    }

    @Override
    public List<String> siteUrls(Site site, long domainId) throws IOException {
        if (site == null) {
            return null;
        }
        long hostId = site.getHostId();
        long siteId = site.getId();
        List<String> urls = new ArrayList<>();
        Site defaultSite = getDefaultSite(hostId);
        HDomain hDomain = domainServiceImpl.infoByIdHostId(domainId, hostId);
        if (hDomain == null) {
            return null;
        }
        String langno = "";
        if (defaultSite.getId().longValue() != site.getId().longValue()) {
            SysLanguage languageBySite = languageServiceImpl.getLanguageBySite(site);
            langno = languageBySite.getLanNo();
        }

        SSL sslData = domainServiceImpl.infoSSL(hostId, domainId);
        String location = (sslData != null ? "https://" : "http://") + hDomain.getDomainName();
        urls.addAll(homeUri(location, langno));

        // page
        List<SPage> listSPage = pageServiceImpl.getInfosByHostSiteId(hostId, siteId);
        if (CollUtil.isEmpty(listSPage)) {
            return null;
        }
        for (SPage sPage : listSPage) {
            urls.add(location + getUri(langno, sPage));
        }

        // category
        for (SPage sPage : listSPage) {
            if (sPage.getSystemId() != null && sPage.getSystemId() > 0) {
                long systemId = sPage.getSystemId();
                SSystem system = systemServiceImpl.getInfoById(systemId, hostId, siteId);
                if (system == null) {
                    continue;
                }
                // 获取该系统下的所有分类
                List<SCategories> listSCategories = categoryServiceImpl.getInfosByHostSiteSystemId(hostId, siteId, systemId);
                for (SCategories sCategories : listSCategories) {
                    urls.add(location + getUri(langno, sPage, sCategories));
                }
                List<String> siteSystemItemUrls = siteSystemItemUrls(hostId, siteId, location, langno, sPage, system);
                urls.addAll(siteSystemItemUrls);
            }
        }

        return urls;
    }

    protected List<String> siteSystemItemUrls(long hostId, long siteId, String location, String langno, SPage sPage, SSystem system)
            throws IOException {
        List<String> urls = new ArrayList<>();
        long systemId = system.getId();
        long systemTypeId = system.getSystemTypeId();
        String url = "";
        if (systemTypeId == 1) {
            // 带分类文章系统
            PageInfo<Article> pageInfoArticle = articleServiceImpl.infosByHostSiteSystemCategoryIdAndPageLimit(hostId, siteId, systemId, -1, "", 1, 1000);
            if (pageInfoArticle != null && pageInfoArticle.getTotal() > 0) {
                List<Article> listArticle = pageInfoArticle.getList();
                if (!CollectionUtils.isEmpty(listArticle)) {
                    for (Article article : listArticle) {
                        url = location + getUri(langno, sPage, article);
                        urls.add(url);
                    }
                }
            }
        } else if (systemTypeId == 2) {
            // 带分类文章系统
            PageInfo<Article> pageInfoArticle = articleServiceImpl.infosByHostSiteSystemCategoryIdAndPageLimit(hostId, siteId, systemId, -1, "", 1, 1000);
            if (pageInfoArticle != null && pageInfoArticle.getTotal() > 0) {
                List<Article> listArticle = pageInfoArticle.getList();
                if (!CollectionUtils.isEmpty(listArticle)) {
                    for (Article article : listArticle) {
                        url = location + getUri(langno, sPage, article);
                        urls.add(url);
                    }
                }
            }
        } else if (systemTypeId == 3) {
            // 带分类产品系统
            PageInfo<ProductDataTableForList> pageInfoProductDataTableForList = productServiceImpl.infosByHostSiteSystemCategoryIdAndPageLimit(hostId, siteId,
                    systemId, -1, "", 1, 1000);
            if (pageInfoProductDataTableForList != null && pageInfoProductDataTableForList.getTotal() > 0) {
                List<ProductDataTableForList> listProductDataTableForList = pageInfoProductDataTableForList.getList();
                if (!CollectionUtils.isEmpty(listProductDataTableForList)) {
                    for (ProductDataTableForList productDataTableForList : listProductDataTableForList) {
                        url = location + getUri(langno, sPage, productDataTableForList);
                        urls.add(url);
                    }
                }
            }
        } else if (systemTypeId == 4) {
            // 不带分类产品系统
            PageInfo<ProductDataTableForList> pageInfoProductDataTableForList = productServiceImpl.infosByHostSiteSystemCategoryIdAndPageLimit(hostId, siteId,
                    systemId, -1, "", 1, 1000);
            if (pageInfoProductDataTableForList != null && pageInfoProductDataTableForList.getTotal() > 0) {
                List<ProductDataTableForList> listProductDataTableForList = pageInfoProductDataTableForList.getList();
                if (!CollectionUtils.isEmpty(listProductDataTableForList)) {
                    for (ProductDataTableForList productDataTableForList : listProductDataTableForList) {
                        url = location + getUri(langno, sPage, productDataTableForList);
                        urls.add(url);
                    }
                }
            }
        }
        return urls;
    }

    protected static String getUri(String langno, SPage sPage, ProductDataTableForList productDataTableForList) {
        return (StringUtils.isEmpty(langno) ? "" : "/" + langno) + SiteUrlUtil.getPath(sPage, productDataTableForList.getSproduct());
    }

    protected static String getUri(String langno, SPage sPage, Article article) {
        return (StringUtils.isEmpty(langno) ? "" : "/" + langno) + SiteUrlUtil.getPath(sPage, article.getSArticle());
    }

    protected static String getUri(String langno, SPage sPage, SCategories category) {
        return (StringUtils.isEmpty(langno) ? "" : "/" + langno) + SiteUrlUtil.getPath(sPage, category);
    }

    protected static String getUri(String langno, SPage sPage) {
        return (StringUtils.isEmpty(langno) ? "" : "/" + langno) + SiteUrlUtil.getPath(sPage);
    }

    protected static List<String> homeUri(String location, String langno) {
        List<String> homes = new ArrayList<>();
        homes.add(location + (StringUtils.isEmpty(langno) ? "/" : "/" + langno + "/"));
        homes.add(location + (StringUtils.isEmpty(langno) ? "/index.html" : "/" + langno + "/index.html"));
        return homes;
    }

    @Override
    public boolean copy(long hostId, long fromSiteId, long toSiteId) throws IOException {
        RestResult restResult = cSiteFeign.copy(hostId, fromSiteId, toSiteId);
        Integer data = ResultUtil.data(restResult, Integer.class);
        if (data != null && data.intValue() == 1) {
            return true;
        }
        return false;
    }

    @Override
    public Integer queryAllCount(int trial, int review) throws IOException {
        RestResult restResult = nodeSiteFeign.queryAllCount(trial, review);
        return RestResultUtil.data(restResult, Integer.class);
    }
}
